import { Plugin_Name, plugin } from '../../components/index.js'
import Data from '../../components/Data.js'
import ActionLog from '../../model/ActionLogFactory.js'

// 注册流程配置：统一管理星级选择与资源路径，避免魔法数字与硬编码
const CONFIG = {
    equipStar: 2,
    paths: {
        defaultUserData: 'resources/data/default/user_data.json',
        defaultEquip: 'resources/data/default/user_equip.json',
        defaultNajie: 'resources/data/default/user_najie.json',
        defaultTongfu: 'resources/data/default/user_tongfu.json',
        defaultStatus: 'resources/data/default/user_status.json',
        defaultArtifacts: 'resources/data/default/user_artifacts.json',
        equipSets: 'resources/data/Equip/装备.json',
        artifactSets: 'resources/data/Artifact/圣遗物.json'
    }
}
// 只读模板与套装数据缓存：降低重复 IO，提高注册性能
const JSON_CACHE = new Map()

// 修仙用户注册插件
export class Registration extends plugin {
    constructor() {
        super({
            name: '修仙_用户注册',
            dsc: '用户注册插件',
            event: 'message',
            priority: 2000,
            rule: [
                {
                    reg: '^#修仙注册(.*)',
                    fnc: 'register'
                }
            ]
        });
    }

    // 注册主流程：解析名号与标识 → 校验 → 加载模板 → 初始化装备/圣遗物
    // → 计算与应用属性 → 串行写入 → 记录日志 → 友好回复
    async register() {
        const uid = this.getUid() // 统一获取用户标识
        const m = this.e.msg.match(/^#修仙注册(.*)$/) // 解析名号参数
        const userName = (m && m[1] ? m[1].trim() : '') || `道友${uid}` // 缺省名号
        if (!uid) {
            await this.e.reply('注册失败：无法获取用户标识')
            return
        }
        try {
            const base = `resources/data/User/${uid}` // 用户数据目录
            const userDataPath = `${base}/data.json` // 用户基本档案
            const exist = this.findExistingUser(uid, userDataPath) // 兼容旧路径的已注册判定
            if (exist && exist.id) {
                await this.e.reply(`道友已在册：${exist['名号'] || userName}`)
                return
            }
            const { tplData, tplEquip, tplNajie, tplTongfu, tplStatus, equipSets, artSets } = await this.loadTemplates(uid, userName) // 模板与套装
            const { items: initEquip, set2: equip2, set4: equip4 } = this.initEquip(equipSets, CONFIG.equipStar) // 初始装备与套装效果
            const { items: initArtifacts, set2: art2, set4: art4, perPieceEff } = this.initArtifacts(artSets, CONFIG.equipStar) // 初始圣遗物与分摊效果
            const updatedData = this.applyStats(tplData, initEquip, equip2, equip4, art2, art4, perPieceEff) // 应用最终属性
            await this.enqueue(uid, async () => {
                Data.writeJSON(userDataPath, updatedData, '\t', '') // 写入用户基础数据
                Data.writeJSON(`${base}/equip.json`, initEquip.length ? initEquip : tplEquip, '\t', '') // 写入初始装备
                tplNajie['信息']['当前容量'] = (tplNajie['信息']['当前容量'] || 0) + initEquip.length // 纳戒容量占用
                Data.writeJSON(`${base}/najie.json`, tplNajie, '\t', '') // 写入纳戒
                Data.writeJSON(`${base}/tongfu.json`, tplTongfu, '\t', '') // 写入同福（随从/伙伴）
                Data.writeJSON(`${base}/status.json`, tplStatus, '\t', '') // 写入状态
                const tplArtifacts = this.safeReadJSON(CONFIG.paths.defaultArtifacts, '') // 读取圣遗物模板
                tplArtifacts['uid'] = uid // 绑定用户
                const equipSlots = { 花: null, 羽: null, 沙: null, 杯: null, 冠: null } // 五件位
                for (const it of initArtifacts) {
                    const part = it['部位']
                    if (Object.prototype.hasOwnProperty.call(equipSlots, part)) equipSlots[part] = it
                }
                tplArtifacts['已装备'] = equipSlots // 当前穿戴
                tplArtifacts['背包'] = [] // 初始背包为空
                const setEffSummary = {} // 套装效果汇总
                for (const [k,v] of Object.entries(art2)) setEffSummary[k] = (setEffSummary[k]||0) + (Number(v)||0)
                for (const [k,v] of Object.entries(art4)) setEffSummary[k] = (setEffSummary[k]||0) + (Number(v)||0)
                tplArtifacts['套装效果汇总'] = setEffSummary
                Data.writeJSON(`${base}/artifacts.json`, tplArtifacts, '\t', '') // 写入圣遗物档案
            })
            await ActionLog.log({ uid, action: '用户注册', source: '系统', layer: undefined, gained: [], lost: [], shouldGain: [], shouldLose: [], success: true, reason: '', context: { 名号: userName } }) // 行为日志：注册
            const gained = [].concat(initEquip.map(x => ({ ...x, 数量: 1 })), initArtifacts.map(x => ({ ...x, 数量: 1 }))) // 初始物品集合
            await ActionLog.log({ uid, action: '用户注册初始化物品', source: '系统', layer: undefined, gained, lost: [], shouldGain: [], shouldLose: [], success: true }) // 行为日志：发放物品
            await this.e.reply(`欢迎加入修仙世界，${userName}已获初始装备与圣遗物`) // 友好回复
        } catch (err) {
            await this.e.reply('注册失败：资源加载或写入异常') // 统一异常提示
        }
    }

    // 提取用户唯一标识：按 user_id → sender.user_id → e.uid 回退
    getUid() {
        const uid = String(this.e.user_id || this.e.sender?.user_id || this.e.uid || '')
        return uid
    }

    // 查找已有用户：优先当前路径，其次兼容旧版路径进行迁移读取
    findExistingUser(uid, userDataPath) {
        let exist = this.safeReadJSON(userDataPath, 'root')
        if (!exist || !exist.id) {
            const oldPath = `resources/users/${uid}/data.json`
            const old = this.safeReadJSON(oldPath, '')
            if (old && old.id) exist = old
        }
        return exist
    }

    // 安全读取 JSON：捕获异常并返回默认值，避免流程中断
    safeReadJSON(path, def) {
        try {
            return Data.readJSON(path, def)
        } catch (_) {
            return typeof def === 'string' ? {} : def
        }
    }

    // 带缓存的读取：对只读资源进行内存缓存，减少重复磁盘 IO
    readJSONCached(path, def) {
        if (JSON_CACHE.has(path)) return JSON_CACHE.get(path)
        const data = this.safeReadJSON(path, def)
        JSON_CACHE.set(path, data)
        return data
    }

    // 加载用户模板与套装数据，并填充 uid / 名号 等基本信息
    async loadTemplates(uid, userName) {
        const tplData = this.readJSONCached(CONFIG.paths.defaultUserData, '')
        const tplEquip = this.readJSONCached(CONFIG.paths.defaultEquip, '')
        const tplNajie = this.readJSONCached(CONFIG.paths.defaultNajie, '')
        const tplTongfu = this.readJSONCached(CONFIG.paths.defaultTongfu, '')
        const tplStatus = this.readJSONCached(CONFIG.paths.defaultStatus, '')
        tplData.id = uid
        tplData['名号'] = userName
        tplNajie['信息'] = tplNajie['信息'] || {}
        tplNajie['信息']['uid'] = uid
        tplTongfu['uid'] = uid
        tplStatus['uid'] = uid
        const equipSetsRaw = this.readJSONCached(CONFIG.paths.equipSets, '')
        const artSetsRaw = this.readJSONCached(CONFIG.paths.artifactSets, '')
        const equipSets = Array.isArray(equipSetsRaw) ? equipSetsRaw : []
        const artSets = Array.isArray(artSetsRaw) ? artSetsRaw : []
        return { tplData, tplEquip, tplNajie, tplTongfu, tplStatus, equipSets, artSets }
    }

    // 初始化装备：选择指定星级套装，挑选部件并携带数值与套装效果（二件/四件）
    initEquip(equipSets, star) {
        const twoStarSet = equipSets.find(s => s['星级'] === star) || {}
        const parts = Array.isArray(twoStarSet['部件']) ? twoStarSet['部件'] : []
        const pickPart = (name) => parts.find(p => p['部位'] === name)
        const pHead = pickPart('头')
        const pShoes = pickPart('鞋子')
        const pWrist = pickPart('护腕')
        const pBelt = pickPart('腰带')
        const pWeapon = pickPart('武器')
        const pRing1 = pickPart('戒指')
        const pRing2 = parts.find(p => p['部位'] === '戒指' && p.id !== (pRing1 && pRing1.id))
        const items = [pHead, pShoes, pWrist, pBelt, pWeapon, pRing1, pRing2].filter(Boolean).map(p => ({
            类别: '装备', 套装名称: twoStarSet['套装名称'], 星级: twoStarSet['星级'], 稀有度: twoStarSet['稀有度'], id: p.id, 名称: p['名称'], 部位: p['部位'],
            生命: Number(p['生命']) || 0,
            防御: Number(p['防御']) || 0,
            速度: Number(p['速度']) || 0,
            生命加成百分比: Number(p['生命加成百分比']) || 0,
            防御加成百分比: Number(p['防御加成百分比']) || 0,
            速度加成百分比: Number(p['速度加成百分比']) || 0,
            暴击率: Number(p['暴击率']) || 0,
            暴击伤害: Number(p['暴击伤害']) || 0
        }))
        const eff = (twoStarSet && twoStarSet['套装效果']) || {}
        const set2 = (eff['二件套'] || {})
        const set4 = (eff['四件套'] || {})
        return { items, set2, set4 }
    }

    // 初始化圣遗物：选择指定星级套装，将套装效果均匀分摊到各部件基础属性
    initArtifacts(artSets, star) {
        const twoStarArt = artSets.find(s => s['星级'] === star) || {}
        const artParts = Array.isArray(twoStarArt['部件']) ? twoStarArt['部件'] : []
        const eff = (twoStarArt && twoStarArt['套装效果']) || {}
        const set2 = (eff['二件套'] || {})
        const set4 = (eff['四件套'] || {})
        const rates = this.getArtifactRates()

        const byPart = {}
        for (const p of artParts) byPart[p['部位']] = p

        const mk = (part, base) => ({
            类别: '圣遗物', 套装名称: twoStarArt['套装名称'], 元素: twoStarArt['元素'], 星级: twoStarArt['星级'], 稀有度: twoStarArt['稀有度'], id: base?.id, 名称: base?.['名称'], 部位: part,
            生命: 0,
            防御: 0,
            速度: 0,
            生命加成百分比: 0,
            防御加成百分比: 0,
            攻击: 0,
            攻击加成百分比: 0,
            速度加成百分比: 0,
            暴击率: 0,
            暴击伤害: 0,
            首领破防百分比: 0,
            元素伤害加成百分比: 0,
            幸运加成百分比: 0,
            副词条: []
        })

        const rnd = (min, max) => Math.round(((min + (max - min) * Math.random())) * 10000) / 10000
        const pickWeighted = (weights) => {
            const entries = Object.entries(weights).filter(([,w]) => Number(w) > 0)
            const sum = entries.reduce((a,[,w]) => a + Number(w), 0)
            let r = Math.random() * sum
            for (const [k,w] of entries) { r -= Number(w); if (r <= 0) return k }
            return entries[entries.length-1]?.[0]
        }

        const items = []

        const partOrder = ['花','羽','沙','杯','冠']
        for (const part of partOrder) {
            const base = byPart[part] || {}
            const it = mk(part, base)
            if (part === '花') {
                const v = rnd(rates.main.flower.min, rates.main.flower.max)
                it['生命'] = v
            } else if (part === '羽') {
                const v = rnd(rates.main.feather.min, rates.main.feather.max)
                it['攻击'] = v
            } else if (part === '沙') {
                const key = pickWeighted(rates.main.sand.weights)
                const v = rnd(rates.main.sand.min, rates.main.sand.max)
                it[key] = v
            } else if (part === '杯') {
                const elem = pickWeighted(rates.main.goblet.element_weights)
                const v = rnd(rates.main.goblet.min, rates.main.goblet.max)
                it['元素伤害加成百分比'] = v
                it['元素'] = elem
            } else if (part === '冠') {
                const key = pickWeighted(rates.main.circlet.weights)
                const v = key === '幸运加成百分比' ? rnd(rates.main.circlet.lucky_min, rates.main.circlet.lucky_max) : rnd(rates.main.circlet.min, rates.main.circlet.max)
                it[key] = v
            }

            const subCount = Math.floor(rates.sub.count_min + Math.random() * (rates.sub.count_max - rates.sub.count_min + 1))
            const allSubKeys = ['生命','防御','攻击','速度','破防','暴击率','暴击伤害','生命加成百分比','防御加成百分比','攻击加成百分比','速度加成百分比','破防加成百分比','幸运加成百分比']
            const weights = {}
            for (const k of allSubKeys) {
                const baseType = k.replace('加成百分比','')
                const wBase = rates.sub.base_weights[baseType] ?? 0.1
                const isPct = /加成百分比$/.test(k)
                weights[k] = wBase * (isPct ? rates.sub.percent_bias : 1)
            }
            const chosen = new Set()
            while (chosen.size < subCount) {
                const key = pickWeighted(weights)
                if (key && !chosen.has(key) && !it[key]) chosen.add(key)
            }
            for (const key of chosen) {
                const isPct = /加成百分比$/.test(key) || key === '幸运加成百分比'
                const min = isPct ? rates.ranges.default_percent_min : rates.ranges.default_flat_min
                const max = isPct ? rates.ranges.default_percent_max : rates.ranges.default_flat_max
                const v = rnd(min, max)
                it[key] = (Number(it[key]) || 0) + v
                it['副词条'].push({ 键: key, 值: v })
            }

            items.push(it)
        }
        return { items, set2, set4, perPieceEff: null }
    }

    getArtifactRates() {
        const cfg = this.safeReadJSON('resources/config/artifact_rates.json', '')
        const def = {
            main: {
                flower: { min: 0.0004, max: 0.0007 },
                feather: { min: 0.0004, max: 0.0007 },
                sand: {
                    weights: {
                        '生命加成百分比': 1.0,
                        '防御加成百分比': 0.9,
                        '攻击加成百分比': 0.8,
                        '速度加成百分比': 0.7,
                        '首领破防百分比': 0.6
                    },
                    min: 0.0004, max: 0.0007
                },
                goblet: {
                    element_weights: { '金': 1, '木': 1, '水': 1, '火': 1, '土': 1 },
                    min: 0.0004, max: 0.0007
                },
                circlet: {
                    weights: {
                        '生命加成百分比': 1.0,
                        '首领破防百分比': 0.6,
                        '暴击率': 0.08,
                        '暴击伤害': 0.07,
                        '幸运加成百分比': 0.05
                    },
                    min: 0.0003, max: 0.0007,
                    lucky_min: 0.0001, lucky_max: 0.0003
                }
            },
            sub: {
                count_min: 2,
                count_max: 3,
                base_weights: {
                    '生命': 1.0,
                    '防御': 0.9,
                    '攻击': 0.8,
                    '速度': 0.7,
                    '破防': 0.6,
                    '暴击率': 0.15,
                    '暴击伤害': 0.15,
                    '幸运': 0.05
                },
                percent_bias: 0.6
            },
            ranges: {
                default_percent_min: 0.0001,
                default_percent_max: 0.0003,
                default_flat_min: 0.0001,
                default_flat_max: 0.0003
            }
        }
        const merge = (a,b) => {
            if (!b || typeof b !== 'object') return a
            const r = Array.isArray(a) ? [...a] : { ...a }
            for (const k of Object.keys(a)) r[k] = merge(a[k], b[k])
            for (const k of Object.keys(b)) if (!(k in a)) r[k] = b[k]
            return r
        }
        return merge(def, cfg)
    }

    // 应用数值到用户模板：统计装备词条 + 叠加套装效果，四舍五入至四位
    applyStats(tplData, initEquip, equip2, equip4, art2, art4, perPieceEff) {
        const sumNum = (arr, key) => arr.reduce((a, b) => a + (Number(b[key]) || 0), 0)
        const round4 = (x) => Math.round(((Number(x) || 0) + 0) * 10000) / 10000
        const lifeFlat = sumNum(initEquip, '生命')
        const defFlat = sumNum(initEquip, '防御')
        const spdFlat = sumNum(initEquip, '速度')
        const lifePct = sumNum(initEquip, '生命加成百分比')
        const defPct = sumNum(initEquip, '防御加成百分比')
        const spdPct = sumNum(initEquip, '速度加成百分比')
        const critRate = sumNum(initEquip, '暴击率')
        const critDmg = sumNum(initEquip, '暴击伤害')
        const pickEff = (obj, k) => Number(obj[k]) || 0
        const addLifePct = pickEff(equip2, '生命加成百分比') + pickEff(equip4, '生命加成百分比') + pickEff(art2, '生命加成百分比') + pickEff(art4, '生命加成百分比')
        const addDefPct = pickEff(equip2, '防御加成百分比') + pickEff(equip4, '防御加成百分比') + pickEff(art2, '防御加成百分比') + pickEff(art4, '防御加成百分比')
        tplData['当前血量'] = round4((Number(tplData['当前血量']) || 0) + lifeFlat)
        tplData['生命加成'] = round4((Number(tplData['生命加成']) || 0) + lifePct + addLifePct)
        tplData['防御加成'] = round4((Number(tplData['防御加成']) || 0) + defPct + addDefPct)
        tplData['速度'] = round4((Number(tplData['速度']) || 0) + spdFlat)
        tplData['速度加成'] = round4((Number(tplData['速度加成']) || 0) + spdPct)
        tplData['暴击率'] = round4((Number(tplData['暴击率']) || 0) + critRate)
        tplData['暴击伤害'] = round4((Number(tplData['暴击伤害']) || 0) + critDmg)
        tplData['血气'] = tplData['当前血量']
        return tplData
    }

    // 同步写入队列：按 uid 串行化文件写入，避免并发覆盖与竞争
    async enqueue(uid, fn) {
        const map = Registration._queue || (Registration._queue = new Map())
        const prev = map.get(uid) || Promise.resolve()
        const next = prev.then(fn, fn)
        map.set(uid, next.catch(() => {}))
        await next
    }
}
